package caramcraft;

import java.util.LinkedList;

import math.geom2d.Vector2D;
import jnibwapi.JNIBWAPI;
import jnibwapi.model.Unit;
import jnibwapi.types.OrderType;
import jnibwapi.types.WeaponType;

public abstract class SCUnit {
	protected final Unit me;
	protected final JNIBWAPI bwapi;
	protected final WeaponType weaponType;
	protected final int maxHealth;
	protected final int maxEnergy;

	protected LinkedList<Unit> myBuildings;
	protected LinkedList<SCUnit> myUnits;

	protected LinkedList<Unit> enemyBuilding;
	protected LinkedList<SCUnit> enemyUnits;

	protected final int attackId = OrderType.OrderTypes.AttackUnit.getID();
	protected long sleep = 0;

	protected SCUnit(Unit me, JNIBWAPI bwapi, WeaponType weaponType) {
		this.me = me;
		this.bwapi = bwapi;
		this.weaponType = weaponType;
		this.maxHealth = me.getHitPoints();
		this.maxEnergy = me.getEnergy();
	}

	protected SCUnit(Unit me, JNIBWAPI bwapi, WeaponType weaponType,
			LinkedList<SCUnit> myUnits, LinkedList<Unit> myBuildings,
			LinkedList<SCUnit> enemyUnits, LinkedList<Unit> enemyBuildings) {
		this.me = me;
		this.bwapi = bwapi;
		this.weaponType = weaponType;
		this.maxHealth = me.getHitPoints();
		this.maxEnergy = me.getEnergy();

		this.myUnits = myUnits;
		this.myBuildings = myBuildings;

		this.enemyBuilding = enemyBuildings;
		this.enemyUnits = enemyUnits;

	}

	abstract public void step(long tick);

	public int getCooldown() {
		return me.getGroundWeaponCooldown();
	}
	
	protected void sleep(long tick, long sleep)
	{
		this.sleep = tick + sleep;
	}
	
	protected boolean isSleep(long tick)
	{
		return tick < sleep;
	}

	public int getDamage() {
		return this.weaponType.getDamageAmount();
	}

	public int getDamage(Unit target) {
		return this.weaponType.getDamageAmount();
	}

	public void move(Vector2D v) {
		bwapi.move(me.getID(), (int) v.x(), (int) v.y());
	}

	public void attack(Unit u) {
		bwapi.attack(me.getID(), u.getID());
	}

	public void attack(Vector2D v) {
		bwapi.attack(me.getID(), (int) v.x(), (int) v.y());
	}

	public void follow(Unit u) {
		bwapi.follow(me.getID(), u.getID());
	}

	public int getAttackRange() {
		return this.weaponType.getMaxRange();
	}

	public void move(Unit u) {
		bwapi.move(me.getID(), u.getX(), u.getY());
	}

	public void stop() {
		bwapi.stop(me.getID());
	}
	
	public boolean isDead()
	{
		return !me.isExists() || me.getHitPoints() <= 0;
	}

	public int getMinAttackRange() {
		return this.weaponType.getMinRange();
	}

	public int getEnergy() {
		return me.getEnergy();
	}

	public int getMaxEnergy() {
		return this.getMaxEnergy();
	}

	public double getEnergyPercent() {
		return (double) getEnergy() / (double) getMaxEnergy();
	}

	public int getHealth() {
		return me.getHitPoints();
	}

	public int getMaxHealth() {
		return this.maxHealth;
	}

	public double getHealthPercent() {
		return (double) getHealth() / (double) this.getMaxHealth();
	}

	public int getID() {
		return me.getID();
	}

	public boolean isAttacking() {
		return me.getOrderID() == attackId || me.isAttackFrame()
				|| me.isStartingAttack() || me.isAttacking();
	}

	public LinkedList<SCUnit> getAttackers() {
		LinkedList<SCUnit> result = new LinkedList<SCUnit>();
		for (SCUnit u : this.enemyUnits) {
			if (u.isAttacking() && u.me.getTargetUnitID() == me.getID())
				result.add(u);
		}
		return result;
	}

	public Unit getUnit() {
		return this.me;
	}

	public LinkedList<Unit> getNeighbors(double radius) {
		LinkedList<Unit> result = new LinkedList<Unit>();

		for (SCUnit u : this.myUnits) {
			if (u.me.getID() == me.getID())
				continue;
			if (radius < getDistance(u.me))
				continue;

			result.add(u.me);
		}

		return result;
	}
	
	public LinkedList<Unit> getEnemyNeighbors(double radius) {
		LinkedList<Unit> result = new LinkedList<Unit>();

		for (SCUnit u : this.enemyUnits) {
			if (u.me.getID() == me.getID())
				continue;
			if (radius < getDistance(u.me))
				continue;

			result.add(u.me);
		}

		return result;
	}

	public double getDistance(Unit target) {
		int diffX = target.getX() - me.getX();
		int diffY = target.getY() - me.getY();
		return Math.sqrt(Math.pow(diffX, 2) + Math.pow(diffY, 2));
	}

	public Vector2D seperate(double radius) {
		Vector2D result = new Vector2D();

		LinkedList<Unit> units = getNeighbors(radius);
		for (Unit u : units) {
			result = result.minus(new Vector2D(u.getX() - me.getX(), u.getY()
					- me.getY()));
		}
		return result;
	}
	
	public Vector2D seperate(LinkedList<SCUnit> units) {
		Vector2D result = new Vector2D();
		for (SCUnit u : units) {
			result = result.minus(u.getPosition().minus(getPosition()));
		}
		return result;
	}

	public SCUnit getNearestEnemy()
	{
		SCUnit result = null;
        double minDistance = Double.POSITIVE_INFINITY;
        for (SCUnit enemy : this.enemyUnits) {
            double distance = getDistance(enemy.me);
            if (distance < minDistance) {
                minDistance = distance;
                result = enemy;
            }
        }
        return result;
	}
	
	public Vector2D moveToUnit(Unit u)
	{
		return new Vector2D(u.getX()-me.getX(),u.getY()-me.getY());
	}
	
	public Vector2D getPosition()
	{
		return new Vector2D(me.getX(),me.getY());
	}

}
